package db;

import java.io.File;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;

import Public.RefObject;

/**
 * Basic Hibernate helper class for Hibernate configuration and startup.
 * <p>
 * Uses a static initializer to read startup options and initialize
 * <tt>Configuration</tt> and <tt>SessionFactory</tt>.
 * <p>
 * This class also tries to figure out if JNDI binding of the
 * <tt>SessionFactory</tt> is used, otherwise it falls back to a global static
 * variable (Singleton). If you use this helper class to obtain a
 * <tt>SessionFactory</tt> in your code, you are shielded from these deployment
 * differences.
 * <p>
 * Another advantage of this class is access to the <tt>Configuration</tt>
 * object that was used to build the current <tt>SessionFactory</tt>. You can
 * access mapping metadata programmatically with this API, and even change it
 * and rebuild the <tt>SessionFactory</tt>.
 * <p>
 * Note: This class supports annotations if you replace the line that creates a
 * Configuration object.
 * <p>
 * Note: This class supports only one data store. Support for several
 * <tt>SessionFactory</tt> instances can be easily added (through a static
 * <tt>Map</tt>, for example). You could then lookup a <tt>SessionFactory</tt>
 * by its name.
 *
 * @author Christian Bauer
 */
public class HibernateUtil
{

	private static Log log = LogFactory.getLog(HibernateUtil.class);

	private static Configuration configuration;
	private static SessionFactory sessionFactory;
	
	private static Configuration configurationDarkSword2;
	private static SessionFactory sessionFactoryDarkSword2;
	
	private static Configuration configurationPlayerbehavior;
	private static SessionFactory sessionFactoryPlayerbehavior;
	
	static
	{
		// Create the initial SessionFactory from the default configuration
		// files
		try
		{
			log.debug("Initializing Hibernate");

			// Read hibernate.properties, if present
			configuration = new Configuration();
			// configuration = new AnnotationConfiguration();
			File f = new File(System.getProperty("user.dir") + File.separator + "config" +"/hibernate.cfg.xml");
			// Read hibernate.cfg.xml (has to be present)
			//configuration.configure(System.getProperty("user.dir") + File.separator + "config" +"/hibernate.cfg.xml");
			configuration.configure(f);
		

			// Build and store (either in JNDI or static variable)
			rebuildSessionFactory(configuration);
			
			configurationDarkSword2=new Configuration();
			File fDS = new File(System.getProperty("user.dir") + File.separator + "config" +"/hibernateDarkSword2.cfg.xml");
			//configurationDarkSword2.configure(System.getProperty("user.dir") + File.separator + "config" +"/hibernateDarkSword2.cfg.xml");
			configurationDarkSword2.configure(fDS);
			
			RefObject<SessionFactory> refObject=new RefObject<SessionFactory>(sessionFactoryDarkSword2);
			rebuildSessionFactory(configurationDarkSword2, refObject);
			sessionFactoryDarkSword2=refObject.argvalue;
			
			configurationPlayerbehavior=new Configuration();
			File fpb = new File(System.getProperty("user.dir") + File.separator + "config" +"/playerbehavior.cfg.xml");
			//configurationDarkSword2.configure(System.getProperty("user.dir") + File.separator + "config" +"/hibernateDarkSword2.cfg.xml");
			configurationPlayerbehavior.configure(fpb);
			
			RefObject<SessionFactory> refObj=new RefObject<SessionFactory>(sessionFactoryPlayerbehavior);
			rebuildSessionFactory(configurationPlayerbehavior, refObj);
			sessionFactoryPlayerbehavior=refObj.argvalue;

			log.debug("Hibernate initialized, call HibernateUtil.getSessionFactory()");
		} catch (Throwable ex)
		{
			// We have to catch Throwable, otherwise we will miss
			// NoClassDefFoundError and other subclasses of Error
			log.error("Building SessionFactory failed.", ex);
			throw new ExceptionInInitializerError(ex);
		}
	}

	/**
	 * Returns the Hibernate configuration that was used to build the
	 * SessionFactory.
	 *
	 * @return Configuration
	 */
	public static Configuration getConfiguration()
	{
		return configuration;
	}

	/**
	 * Returns the global SessionFactory either from a static variable or a JNDI
	 * lookup.
	 *
	 * @return SessionFactory
	 */
	public static SessionFactory getSessionFactory()
	{
		String sfName = configuration
				.getProperty(Environment.SESSION_FACTORY_NAME);
		if (sfName != null)
		{
			log.debug("Looking up SessionFactory in JNDI");
			try
			{
				return (SessionFactory) new InitialContext().lookup(sfName);
			} catch (NamingException ex)
			{
				throw new RuntimeException(ex);
			}
		}
		else if (sessionFactory == null)
		{
			rebuildSessionFactory();
		}
		return sessionFactory;
	}
	
	public static SessionFactory getDarkSword2SessionFactory()
	{
		String sfName = configurationDarkSword2
				.getProperty(Environment.SESSION_FACTORY_NAME);
		if (sfName != null)
		{
			log.debug("Looking up SessionFactory in JNDI");
			try
			{
				return (SessionFactory) new InitialContext().lookup(sfName);
			} catch (NamingException ex)
			{
				throw new RuntimeException(ex);
			}
		}
		else if (sessionFactoryDarkSword2 == null)
		{
			RefObject<SessionFactory> refObject=new RefObject<SessionFactory>(sessionFactoryDarkSword2);
			rebuildSessionFactory(configurationDarkSword2, refObject);
			sessionFactoryDarkSword2=refObject.argvalue;
		}
		return sessionFactoryDarkSword2;
	}
	
	public static SessionFactory getPlayerhaviorSessionFactory()
	{
		String sfName = configurationPlayerbehavior
				.getProperty(Environment.SESSION_FACTORY_NAME);
		if (sfName != null)
		{
			log.debug("getPlayerhaviorSessionFactory --- Looking up SessionFactory in JNDI");
			try
			{
				return (SessionFactory) new InitialContext().lookup(sfName);
			} catch (NamingException ex)
			{
				throw new RuntimeException(ex);
			}
		}
		else if (sessionFactoryPlayerbehavior == null)
		{
			RefObject<SessionFactory> refObject=new RefObject<SessionFactory>(sessionFactoryPlayerbehavior);
			rebuildSessionFactory(configurationPlayerbehavior, refObject);
			sessionFactoryPlayerbehavior=refObject.argvalue;
		}
		return sessionFactoryPlayerbehavior;
	}
	
	/**
	 * Closes the current SessionFactory and releases all resources.
	 * <p>
	 * The only other method that can be called on HibernateUtil after this one
	 * is rebuildSessionFactory(Configuration).
	 */
	public static void shutdown()
	{
		log.debug("Shutting down Hibernate");
		// Close caches and connection pools
		getSessionFactory().close();

		// Clear static variables
		sessionFactory = null;
	}

	/**
	 * Rebuild the SessionFactory with the static Configuration.
	 * <p>
	 * Note that this method should only be used with static SessionFactory
	 * management, not with JNDI or any other external registry. This method
	 * also closes the old static variable SessionFactory before, if it is still
	 * open.
	 */
	public static void rebuildSessionFactory()
	{
		log.debug("Using current Configuration to rebuild SessionFactory");
		rebuildSessionFactory(configuration);
	}

	/**
	 * Rebuild the SessionFactory with the given Hibernate Configuration.
	 * <p>
	 * HibernateUtil does not configure() the given Configuration object, it
	 * directly calls buildSessionFactory(). This method also closes the old
	 * static variable SessionFactory before, if it is still open.
	 *
	 * @param cfg
	 */
	public static void rebuildSessionFactory(Configuration cfg)
	{
		log.debug("Rebuilding the SessionFactory from given Configuration");
		if (sessionFactory != null && !sessionFactory.isClosed())
			sessionFactory.close();
		if (cfg.getProperty(Environment.SESSION_FACTORY_NAME) != null)
		{
			log.debug("Managing SessionFactory in JNDI");
			cfg.buildSessionFactory();
		}
		else
		{
			log.debug("Holding SessionFactory in static variable");
			sessionFactory = cfg.buildSessionFactory();
		}
		configuration = cfg;
	}
	
	
	public static void rebuildSessionFactory(Configuration cfg,RefObject<SessionFactory> mSessionFactory)
	{
		log.debug("Rebuilding the SessionFactory from given Configuration");
		if (mSessionFactory.argvalue != null && !mSessionFactory.argvalue.isClosed())
			mSessionFactory.argvalue.close();
		if (cfg.getProperty(Environment.SESSION_FACTORY_NAME) != null)
		{
			log.debug("Managing SessionFactory in JNDI");
			cfg.buildSessionFactory();
		}
		else
		{
			log.debug("Holding SessionFactory in static variable");
			
			mSessionFactory.argvalue = cfg.buildSessionFactory();
		}
	}

}
